#ifndef __M_SS_H__
#define __M_SS_H__
#include "util.hpp"

typedef enum{UNLOGIN , LOGIN} ss_statu;
//session类用于保存客户端的用户状态信息
class session{
    private:
        uint64_t _ssid; //sessionID
        uint64_t _uid; //session所对应的用户ID
        ss_statu _statu; //用户状态：未登录，已登录
        wsserver_t::timer_ptr _tp;//session相关的定时器（通过判断session对象是否包含定时器，确定其是否添加定时销毁任务）
    public:
        //初始化ssid和析构
        session(uint64_t ssid)
        :_ssid(ssid){
            DLOG("SESSION %p 被创建！！", this);//打印session对象的地址
        }
        ~session(){DLOG("SESSION %p 被释放！！", this); }
        //设置用户状态，用户id，session相关定时器
        void set_statu(ss_statu statu){_statu = statu;}
        void set_user(uint64_t uid){_uid = uid;}
        void set_timer(const wsserver_t::timer_ptr &tp){_tp = tp;}
        //获取session相关信息
        uint64_t ssid() { return _ssid; }
        uint64_t get_user() { return _uid; }
        wsserver_t::timer_ptr& get_timer() { return _tp; }
        //判断用户状态
        bool is_login() { return (_statu == LOGIN); }
};

#define SESSION_TIMEOUT 30000   //session销毁时间
#define SESSION_FOREVER -1      //session永久存在的标识
using session_ptr = std::shared_ptr<session>;//通过智能指针来维护session类
//session_manager类用来管理创建的session类
class session_manager{
    private:
        uint64_t _next_ssid;
        std::mutex _mutex;
        std::unordered_map<uint64_t,session_ptr> _session;//通过哈希表来维护ssid和session对象的映射
        wsserver_t *_server;//定义一个指向WebSocket服务器的指针
        
    public:
        session_manager(wsserver_t *srv)
        :_next_ssid(1),_server(srv){
            DLOG("session管理器初始化完毕！");
        }
        ~session_manager(){ DLOG("session管理器即将销毁！");}

        //创建session
        session_ptr create_session(uint64_t uid,ss_statu statu){
            std::unique_lock<std::mutex> lock(_mutex);
            session_ptr ssp(new session(_next_ssid));//创建一个session对象ssp用智能指针来维护
            ssp->set_statu(statu);
            ssp->set_user(uid);
            _session.insert(std::make_pair(_next_ssid,ssp));
            _next_ssid++;
            return ssp;
        }

        //添加一个已经存在的session
        void append_session(const session_ptr &ssp){
            std::unique_lock<std::mutex> lock(_mutex);
            _session.insert(std::make_pair(ssp->ssid(),ssp));
        }

        //通过ssid获取session信息
        session_ptr get_session_by_ssid(uint64_t ssid){
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _session.find(ssid);
            if (it == _session.end()) {
                return session_ptr();
            }
            return it->second;
        }

        //移除session
        void remove_session(uint64_t ssid){
            std::unique_lock<std::mutex> lock(_mutex);
            _session.erase(ssid);
        }

        //设置session定时器
        void set_session_expire_time(uint64_t ssid,int ms){
            //依赖于websocketpp的定时器来完成session生命周期的管理。
            // 登录之后，创建session，session需要在指定时间无通信后删除
            // 但是进入游戏大厅，或者游戏房间，这个session就应该永久存在
            // 等到退出游戏大厅，或者游戏房间，这个session应该被重新设置为临时，在长时间无通信后被删除

            //先获取session
            session_ptr ssp = get_session_by_ssid(ssid);
            if(ssp.get() == nullptr){
                return;
            }
            wsserver_t::timer_ptr tp = ssp->get_timer();

            if(tp.get()==nullptr && ms == SESSION_FOREVER){
                //1. 在session永久存在的情况下，设置永久存在
                return;

            }else if(tp.get()==nullptr && ms != SESSION_FOREVER){
                //2. 在session永久存在的情况下，设置指定时间之后被删除的定时任务
                wsserver_t::timer_ptr tmp_tp =_server->set_timer(ms,
                    std::bind(&session_manager::remove_session,this,ssid));
                    ssp->set_timer(tmp_tp);

            }else if(tp.get()!=nullptr && ms == SESSION_FOREVER){
                //3. 在session设置了定时删除的情况下，将session设置为永久存在
                // 删除定时任务--- stready_timer删除定时任务会导致任务(删除一个session)直接被执行
                tp->cancel();//由于这个取消定时任务并不是立即取消的
                //因此重新给session管理器中，添加一个session信息, 且添加的时候需要使用定时器，而不是立即添加，这样才能保证代码的顺序执行
                ssp->set_timer(wsserver_t::timer_ptr());//将session关联的定时器设置为空
                _server->set_timer(0,std::bind(&session_manager::append_session, this, ssp));

            }else if(tp.get()!=nullptr && ms != SESSION_FOREVER){
                //4. 在session设置了定时删除的情况下，将session重置删除时间。
                tp->cancel();//因为这个取消定时任务并不是立即取消的
                ssp->set_timer(wsserver_t::timer_ptr());
                _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));

                //重新给session添加定时销毁任务
                wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms, 
                    std::bind(&session_manager::remove_session, this, ssp->ssid()));
                //重新设置session关联的定时器
                ssp->set_timer(tmp_tp);    
            }

        }
};


#endif